﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Linq.Expressions;
using SoGame.CommandManager;
using System.ServiceModel;
using EventAggregator;
using SoGame.WebServices.Host.Messages;

namespace SoGame.WebServices.Host
{
    public class ServiceStatusModel : INotifyPropertyChanged, IDisposable
    {
        private string name;
        private bool isStart = false;
        private ServiceHost service;
        private BindingList<Uri> baseAddresses;
        private IEventAggregator events;

        /// <summary>
        /// Initializes a new instance of the <see cref="ServiceStatusModel"/> class.
        /// </summary>
        /// <param name="events">The events.</param>
        public ServiceStatusModel(IEventAggregator events) {
            this.events = events;
            this.baseAddresses = new BindingList<Uri>();
            this.Name = string.Empty;

            //this.StartServiceCommand.Execute();
        }

        /// <summary>
        /// Initialize the service.
        /// </summary>
        private void ServiceInitialisation()
        {
            service = new ServiceHost(typeof(SoGame));
            service.Opened += (s, e) => HandleStateChange(service.State);
            service.Closed += (s, e) => HandleStateChange(service.State);
            service.Opening += (s, e) => HandleStateChange(service.State);
            service.Closing += (s, e) => HandleStateChange(service.State);
            service.Faulted += (s, e) => HandleStateChange(service.State);

            this.Name = this.service.Description.Name;
            this.BaseAddresses.Clear();
            this.service.BaseAddresses.ToList().ForEach(i => this.BaseAddresses.Add(i));
            this.OnPropertyChanged(() => this.BaseAddresses);

        }

        /// <summary>
        /// Dispose the service.
        /// </summary>
        private void ServiceDispose() {
            service.Opened -= (s, e) => HandleStateChange(service.State);
            service.Closed -= (s, e) => HandleStateChange(service.State);
            service.Opening -= (s, e) => HandleStateChange(service.State);
            service.Closing -= (s, e) => HandleStateChange(service.State);
            service.Faulted -= (s, e) => HandleStateChange(service.State);
            this.BaseAddresses.Clear();
            ((IDisposable)service).Dispose();
            this.OnPropertyChanged(() => this.BaseAddresses);
        }

        /// <summary>
        /// Handles the state change.
        /// </summary>
        /// <param name="state">The state.</param>
        protected void HandleStateChange(CommunicationState state) {
            this.IsStart = state == CommunicationState.Opened;
            events.Publish(new ChangeStateMessage() { State = state });
        }

        /// <summary>
        /// Gets or sets the description of the service.
        /// </summary>
        /// <value>
        /// The name of the service.
        /// </value>
        public string Name {
            get { return this.name; }
            private set {
                if (this.name != value) {
                    this.name = value;
                    OnPropertyChanged(() => this.Name);
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is start.
        /// </summary>
        /// <value>
        ///   <c>true</c> if this instance is start; otherwise, <c>false</c>.
        /// </value>
        public bool IsStart {
            get { return this.isStart; }
            set {
                if (this.isStart != value) {
                    this.isStart = value;
                    this.OnPropertyChanged(() => this.IsStart);
                }
            }
        }

        /// <summary>
        /// Gets the baseAddresses.
        /// </summary>
        public BindingList<Uri> BaseAddresses {
            get { return this.baseAddresses; }
        }

        #region Command
        /// <summary>
        /// Gets the start service command.
        /// </summary>
        public ActionCommand StartServiceCommand {
            get {
                return new ActionCommand("Start service", () => !this.isStart, () => { ServiceInitialisation(); this.service.Open(); });
            }
        }

        /// <summary>
        /// Gets the stop service command.
        /// </summary>
        public ActionCommand StopServiceCommand {
            get {
                return new ActionCommand("Stop service", () => this.isStart, () => { this.service.Close(); ServiceDispose(); });
            }
        }
        #endregion

        #region INotifyPropertyChanged
        /// <summary>
        /// Called when [property changed].
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="action">The action.</param>
        protected void OnPropertyChanged<T>(Expression<Func<T>> action)
        {
            var propertyName = GetPropertyName(action);
            OnPropertyChanged(propertyName);
        }

        /// <summary>
        /// Called when [property changed].
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        private void OnPropertyChanged(string propertyName)
        {
            //if ((new List<string> { "FirstName", "Name"}).Contains(propertyName)) {
            //    this.SearchString = string.Concat(this.FirstName, " ", this.Name);
            //}

            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                var e = new PropertyChangedEventArgs(propertyName);
                handler(this, e);
            }
        }

        /// <summary>
        /// Gets the name of the property.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="action">The action.</param>
        /// <returns></returns>
        private static string GetPropertyName<T>(Expression<Func<T>> action)
        {
            var expression = (MemberExpression)action.Body;
            var propertyName = expression.Member.Name;
            return propertyName;
        }

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            if (this.service != null && this.service.State == CommunicationState.Opened) {
                this.service.Close();
            }
            ((IDisposable)this.service).Dispose();
        }
    }
}
